package com.demo;

import com.demo.domain.Author;
import com.demo.domain.Book;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author gxd
 * @since 2025/5/27
 */
public class StreamTest {

    public static void main(String[] args) {
        // Stream流式函数式变成，对集合或数组进行链状流式操作
        List<Author> authors = getAuthors();
        System.out.println("authors = " + authors);

        // 创建流方式
        // 方式一；单列集合，使用方式：集合对象.stream()
        Stream<Author> stream = authors.stream();
        // 方式二：数组，使用方式：Arrays.stream(数组)或者使用Stream.of
        Integer[] arr = {1,2,3,4,5};
        Stream<Integer> stream1 = Arrays.stream(arr);
        Stream<Integer> stream2 = Stream.of(arr);
        // 双列集合：转成单列集合，使用方式：集合对象.stream()
        Map<String, Integer> map = new HashMap<>();
        map.put("zs", 12);
        map.put("ls", 22);
        map.put("wu", 32);
        Stream<Map.Entry<String, Integer>> stream3 = map.entrySet().stream();

        // 中间操作
        // filter：对流中元素进行条件过滤，符合过滤条件的才能留在流中
        // 示例：打印所有姓名长度大于1的作家的姓名
        System.out.println("filter 示例：打印所有姓名长度大于1的作家的姓名");
        authors.stream()
                .filter(author -> author.getName().length() > 1)
                .forEach(author -> System.out.println(author.getName()));
        // map：对流中元素进行计算或转换
        // 示例：打印所有作家的名字
        System.out.println("map 示例：打印所有作家的名字");
        authors.stream()
                .map(Author::getName)
                .forEach(System.out::println);
        // 示例，打印所有作家的年龄加10后的年龄
        System.out.println("map 示例，打印所有作家的年龄加10后的年龄");
        authors.stream()
                .map(author -> author.getAge() + 10)
                .forEach(System.out::println);
        // distinct：去除流中重复元素
        // 示例：打印所有作家的名称，并去掉重复元素
        System.out.println("distinct 示例：打印所有作家的名称，并去掉重复元素");
        authors.stream()
                .distinct()
                .forEach(author -> System.out.println(author.getName()));
        // sorted：对流元素进行排序
        // 示例：对流中数据按照年龄进行降序排序，并且不能有重复的元素
        System.out.println("sorted 示例：对流中数据按照年龄进行降序排序，并且不能有重复的元素");
        authors.stream()
                .distinct()
                .sorted((o1, o2) -> o2.getAge() - o1.getAge())
                .forEach(author -> System.out.println(author.getAge()));
        // limit：设置流的最大长度，超出部分将被抛弃
        // 示例：对流中元素按照年龄进行降序排序，要求不能有重复元素，打印年龄最大的两个作家的姓名。
        System.out.println("limit 示例：对流中元素按照年龄进行降序排序，要求不能有重复元素，打印年龄最大的两个作家的姓名。");
        authors.stream()
                .distinct()
                .sorted((o1, o2) -> o2.getAge() - o1.getAge())
                .limit(2)
                .forEach(author -> System.out.println(author.getName()));
        // skip：跳过流中前n个元素，返回剩下元素
        // 示例：打印除了年龄最大的作家外的其他作家，要求不能有重复元素，并且按照年龄降序排序。
        System.out.println("skip 示例：打印除了年龄最大的作家外的其他作家，要求不能有重复元素，并且按照年龄降序排序。");
        authors.stream()
                .distinct()
                .sorted((o1, o2) -> o2.getAge() - o1.getAge())
                .skip(1)
                .forEach(author -> System.out.println(author.getName()));
        // flatMap：map只能把一个对象转成另一个对象来作为流中的元素。而flatMap可以把一个对象转成多个对象作为流中的元素。
        // 示例一 对应所有书籍的名字。要求对重复元素进行去重
        System.out.println("flatMap 示例一 对应所有书籍的名字。要求对重复元素进行去重");
        authors.stream()
                .flatMap(author -> author.getBooks().stream())
                .distinct()
                .forEach(book -> System.out.println(book.getName()));
        // 示例二 打印现有数据的所有分类。要求对分类进行去重。不能出现这种格式：哲学,爱情
        System.out.println("flatMap 示例二 打印现有数据的所有分类。要求对分类进行去重。不能出现这种格式：哲学,爱情");
        authors.stream()
                .flatMap(author -> author.getBooks().stream())
                .distinct()
                .flatMap(book -> Arrays.stream(book.getCategory().split(",")))
                .distinct()
                .forEach(System.out::println);

        // 终结操作
        // foreach：对流中元素进行遍历擦欧洲哦
        // 示例 输出所有作家的名字
        System.out.println("foreach 示例 输出所有作家的名字");
        authors.stream()
                .map(Author::getName)
                .distinct()
                .forEach(System.out::println);
        // count：用来获取当前流中元素的个数
        // 示例 打印这些作家的所有书籍的数目，注意删除重复元素
        System.out.println("count 示例 打印这些作家的所有书籍的数目，注意删除重复元素");
        long count = authors.stream()
                .flatMap(author -> author.getBooks().stream())
                .distinct()
                .count();
        System.out.println(count);
        // max&min：可以用来获取流中的最值
        // 示例：分别获取这些作家的所有书籍的最高分和最低分并打印
        System.out.println("max&min 示例：分别获取这些作家的所有书籍的最高分和最低分并打印");
        Optional<Integer> max = authors.stream()
                .flatMap(author -> author.getBooks().stream())
                .map(Book::getScore)
                .max((score1, score2) -> score1 - score2);
        System.out.println(max.get());

        Optional<Integer> min = authors.stream()
                .flatMap(author -> author.getBooks().stream())
                .map(Book::getScore)
                .min(((score1, score2) -> score1 - score2));
        System.out.println(min.get());
        // collect：把当前流转成一个集合
        // 示例一：获取一个存放所有作者名字的List集合
        System.out.println("collect 示例一：获取一个存放所有作者名字的List集合");
        List<String> list = authors.stream()
                .map(Author::getName)
                .collect(Collectors.toList());
        System.out.println(list);
        // 示例二：获取一个所有书名的Set集合
        System.out.println("collect 示例二：获取一个所有书名的Set集合");
        Set<String> setList = authors.stream()
                .flatMap(author -> author.getBooks().stream())
                .map(Book::getName)
                .collect(Collectors.toSet());
        System.out.println(setList);
        // 示例三：获取一个Map集合，map的key为作者名，value为List<Book>
        System.out.println("collect 示例三：获取一个Map集合，map的key为作者名，value为List<Book>");
        Map<String, List<Book>> mapList = authors.stream()
                .distinct()
                .collect(Collectors.toMap(author -> author.getName(), author -> author.getBooks()));
        System.out.println(mapList);
        // 查询与匹配
        // anyMatch；可以用来判断是否有任意符合匹配条件的元素，结果是boolean类型
        // 示例 判断是否有年龄在29以上的作家
        System.out.println("anyMath 示例 判断是否有年龄在29以上的作家");
        boolean result1 = authors.stream()
                .anyMatch(author -> author.getAge() > 29);
        System.out.println(result1);
        // allMatch:可以用来判断是否都符合匹配条件，结果为boolean类型。如果都符合结果为true，否则结果为false
        // 示例 判断是否所有的作家都是成年人
        System.out.println("allMatch 示例 判断是否所有的作家都是成年人");
        boolean result2 = authors.stream()
                .allMatch(author -> author.getAge() >= 18);
        System.out.println(result2);
        // noneMatch: 判断流中元素是否都不符合匹配条件，如果都不符合结果为true，否则结果为false
        // 示例 判断作家是否都没有超过100岁的
        System.out.println("noneMatch: 示例 判断作家是否都没有超过100岁的");
        boolean result3 = authors.stream()
                .noneMatch(author -> author.getAge() < 100);
        System.out.println(result3);
        // findAny：获取流中的任意一个元素。该方法没有办法保证获取的一定是流中的第一个元素。
        // 示例：获取任意一个大于18的作家，如果存在就输出他的名字
        System.out.println("findAny 示例：获取任意一个大于18的作家，如果存在就输出他的名字");
        Optional<Author> optionalAuthor = authors.stream()
                .filter(author -> author.getAge() > 18)
                .findAny();
        optionalAuthor.ifPresent(author ->  System.out.println(author.getName()));
        // findFirst：获取流中的第一个元素
        // 示例 获取一个年龄最小的作家，并输出他的姓名
        System.out.println("findFirst 示例：获取一个年龄最小的作家，并输出他的姓名");
        Optional<Author> first = authors.stream()
                .sorted((o1, o2) -> o1.getAge() - o2.getAge())
                .findFirst();
        first.ifPresent(author -> System.out.println(author.getName()));
        // reduce归并；对流中的数据按照你制定的计算方式计算出一个结果
        // reduce的最用是把stream中的元素组合起来,我们可以传入一个初始值，他会按照我们的计算方式一次拿流中的元素和初始化值上进行计算，计算结果再和后面的元素计算。
        // 内部的计算方式如下：
        // T result = result;
        // for (T element : this stream) {
        //    result = accumulator.apply(result, element);
        // }
        // return result;
        // 其中identity就是我们可以通过方法参数传入的初始值，accumulator的apply具体进行什么计算也是我们通过方法参数来确定的。
        // 示例一：使用reduce求所有作者年龄的和
        System.out.println("reduce 示例一：使用reduce求所有作者年龄的和");
        Integer sum = authors.stream()
                .distinct()
                .map(Author::getAge)
                .reduce(0, (result, element) -> result + element);
        System.out.println(sum);
        // 示例二：使用reduce求所有作者中年龄的最大值
        System.out.println("reduce 示例二：使用reduce求所有作者中年龄的最大值");
        Integer maxAge = authors.stream()
                .map(Author::getAge)
                // .reduce(Integer::max).get();
                .reduce(Integer.MIN_VALUE, (result, element) -> result > element ? result : element);
        System.out.println(maxAge);
        // 示例三：使用reduce求所有作者中年龄的最小值
        System.out.println("reduce 示例三：使用reduce求所有作者中年龄的最小值");
        // Integer minAge = authors.stream()
        //         .map(Author::getAge)
        //         // .reduce(Integer::min).get();
        //         .reduce(Integer.MAX_VALUE, (result, element) -> result < element ? result : element);
        Optional<Integer> minOptional = authors.stream()
                .map(Author::getAge)
                .reduce((result, element) -> result < element ? result : element);
        minOptional.ifPresent(System.out::println);

        // 示例一：打印所有年龄小于18的作者名字，并且去重
        System.out.println("示例一：打印所有年龄小于18的作者名字，并且去重");
        authors.stream()
                .distinct()
                .filter(author -> author.getAge() < 18)
                .map(Author::getName)
                .forEach(System.out::println);

        /*// 不使用stream的写法
        Set<String> result = new HashSet<>();
        for (Author author : authors) {
            if (author.getAge() < 18) {
                result.add(author.getName());
            }
        }
        result.forEach(System.out::println);*/

        // 并行流 parallel方法可以把串行流转为并行流，适用于大数据量
        System.out.println("并行流 parallel使用");
        Stream<Integer> stream4 = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        Integer sumNum = stream4.parallel()
                .peek(num -> System.out.println(num + "::" + Thread.currentThread().getName()))
                .filter(num -> num > 5)
                .reduce((result, element) -> result + element)
                .get();
        System.out.println(sumNum);
        System.out.println("并行流 parallelStream使用");
        Integer sumNum2 = authors.parallelStream()
                .map(Author::getAge)
                .peek(num -> System.out.println(num + "::" + Thread.currentThread().getName()))
                .filter(age -> age > 5)
                .reduce((result, element) -> result + element)
                .get();
        System.out.println(sumNum2);
    }

    // 初始化数据
    private static List<Author> getAuthors() {
        Author author = new Author(1L, "张三", 33, "一名犯罪狂人", null);
        Author author2 = new Author(2L, "李四", 15, "一名愚者", null);
        Author author3 = new Author(3L, "王", 16, "一名旅行者", null);
        Author author4 = new Author(3L, "王", 16, "一名旅行者", null);

        List<Book> books1 = new ArrayList<>();
        List<Book> books2 = new ArrayList<>();
        List<Book> books3 = new ArrayList<>();

        books1.add(new Book(1L, "阿大发送到发送到发送到", "哲学,爱情", 25, "afasdfsdaf"));
        books1.add(new Book(2L, "asdfsdfdsfds", "个人成长,爱情", 100, "adsfadsfsadf"));

        books2.add(new Book(3L, "风吹不到的地方", "哲学", 150, "adsfadsfsadf"));
        books2.add(new Book(3L, "风吹不到的地方", "哲学", 150, "adsfadsfsadf"));
        books2.add(new Book(4L, "吹哦阿帆", "爱情,个人传记", 20, "adsfadsfsadf"));

        books3.add(new Book(5L, "你的啊大师傅三", "爱情", 147, "adsfadsfsadf"));
        books3.add(new Book(6L, "你的啊傅三", "个人传记", 147, "adsfadsfsadf"));
        books3.add(new Book(6L, "你师傅三", "个人传记", 42, "adsfadsfsadf"));

        author.setBooks(books1);
        author2.setBooks(books2);
        author3.setBooks(books3);
        author4.setBooks(books3);

        List<Author> authorList = new ArrayList<>(Arrays.asList(author, author2, author3, author4));
        return authorList;
    }
}
